package flexo.physic;

import java.util.Hashtable;

import flexo.math.Vector;
import flexo.physic.collision.CollisionDetector;
import flexo.physic.entity.Entity;
import flexo.physic.entity.Sphere;
import flexo.physic.force.*;
import flexo.scene.Node;

public class PhysicSystem extends flexo.common.System
{
	public static final String ID = "physic";
	
	protected float globalGravity;
	
	protected java.util.List<Entity> entities;
	protected java.util.Hashtable<String, Force> forces;
	
	protected ContactResolver solver;
	
	public PhysicSystem()
	{
		super(ID,flexo.common.System.UPDATE_MAX);
		
		setGlobalGravity(9.8f);
		
		entities = new java.util.LinkedList<Entity>();
		forces = new Hashtable<String, Force>();
		solver = new ContactResolver();
	}
	
	public void setGlobalGravity(float value)
	{
		globalGravity = value;
	}
	
	public Sphere addSphere(float mass, float radius)
	{
		Sphere e = new Sphere(this.globalGravity, mass, radius);
		entities.add(e);
		return e;
	}
	
	public SimpleForce addSimpleForce(String name, Vector direction)
	{
		SimpleForce f = new SimpleForce(name, direction);
		forces.put(name, f);
		return f;
	}
	
	public Drag addDragForce(String name, float k1, float k2)
	{
		Drag f = new Drag(name,k1,k2);
		forces.put(name,f);
		return f;
	}
	
	public Spring addSpringForce(String name, Node hook, float length)
	{
		Spring f = new Spring(name,hook,length);
		forces.put(name,f);
		return f;
	}
	
	public Bungee addBungeeForce(String name, Node hook, float length)
	{
		Bungee f = new Bungee(name,hook,length);
		forces.put(name,f);
		return f;
	}
	
	public Force getForce(String name)
	{
		return forces.get(name);
	}
	
	public void removeForce(String name)
	{
		forces.remove(name);
	}
	
	public void removeEntity(Entity e)
	{
		entities.remove(e);
	}
	
	public void update()
	{
		// Apply forces
		for (Entity e: entities)
		{
			e.integrate(this.getElapsedTime());
		}
		
		// Calculate collisions
		for (Entity e1: entities)
		{
			for (Entity e2: entities)
			{
				if (e1 != e2)
				{
					// Close contact
					/*
					Vector distance = Vector.getTempVector();
					Vector.sub(e1.getPosition(), e2.getPosition(), distance);
					float fDistance = distance.getLength();
					float cDistance = (e1.getSize() + e2.getSize())/2;
					distance.normalize();
					if (fDistance < cDistance)
					*/
					ContactData cd = CollisionDetector.check(e1.getCollisionShape(), e2.getCollisionShape()); 
					if (cd != null)
					{
						solver.addContact(new Contact(e1,e2,1,cd.penetration,cd.normal));
					}					
				}
				
				// TODO: Add other for loop to check collision against scenery
			}
		}
		
		// Resolve collisions
		solver.resolveContacts(this.getElapsedTime());
	}
}
